﻿
using System.Collections.Generic;
using System.Runtime.InteropServices.ComTypes;
using UnityEngine;

namespace XFGameFramework.OctreeSystem
{ 
    // 一个游戏物体是可以存到多个区域的(后面再升级) 
    public class OctreeObject : MonoBehaviour
    { 

        #region 字段

        [Tooltip("在八叉树空间中的位置")]
        private Vector3 position;

        [SerializeField]
        [Tooltip("是否在移除八叉树空间中的游戏物体时更新八叉树的格子信息?")]
        private bool updateBoundsOnRemove = false;

        private List<OctreeBounds> boundsList;

        private Bounds bounds ;

        [Tooltip("与该游戏物体相交的游戏物体")]
        private List<OctreeObject> intersectObjects;

        [SerializeField]
        [Tooltip("能够相交的游戏物体层级!")]
        private LayerMask layerMask = Physics.AllLayers;

        //private float lastUpdateTime = 0;

        #endregion


        #region 属性

        public Vector3 Position
        {
            get
            {
                return position;
            }
            set
            {  
                // 修改位置
                transform.position = value;
                if (position == value) return; 
                if (!Enable) return;
                if (!gameObject.activeInHierarchy) return; 

                // 在设置位置之前先移除
                OctreeManager.Remove(this, updateBoundsOnRemove); 
                position = value; 
                // 添加到八叉树空间
                OctreeManager.Add(this);
            }
        }

        public List<OctreeObject> IntersectObjects
        {
            get 
            {
                if(intersectObjects == null)
                    intersectObjects = new List<OctreeObject>();
                 
                Bounds bounds = GetBounds(); 
                OctreeManager.Get(bounds, intersectObjects, layerMask);

                for (int i = intersectObjects.Count - 1; i >= 0; i--)
                {
                    if (!intersectObjects[i]) continue;
                    if (intersectObjects[i] == this) continue;
                     
                    // 如果不相交 直接移除即可
                    if (!intersectObjects[i].Intersects(this))
                        intersectObjects.RemoveAt(i);
                }

                return intersectObjects;
            }
        }


        public bool Enable { get;private set; }

        /// <summary>
        /// 所在的区域
        /// </summary>
        internal OctreeArea Area { get; set; }

        #endregion

        #region 方法
         
        public bool Intersects(Bounds bounds) 
        { 
            if (boundsList != null && boundsList.Count > 0)
            { 
                for (int i = 0; i < boundsList.Count; i++)
                {
                    if( boundsList[i].Intersects(bounds))
                        return true;
                }
            }
            else
            {
                return bounds.Contains(position);
            }

            return false;
        }

        public bool Intersects(OctreeObject other) 
        {
            if (boundsList != null && boundsList.Count > 0)
            {
                foreach (var bounds in boundsList)
                {
                    if (other.boundsList != null && other.boundsList.Count > 0)
                    {
                        foreach (var other_bounds in other.boundsList)
                        {
                            if (bounds.Intersects(other_bounds))
                                return true;
                        }
                    }
                    else
                    {
                        if (bounds.Contains(other.Position))
                            return true;
                    }
                }
            }
            else 
            {
                if (other.boundsList != null && other.boundsList.Count > 0)
                {
                    foreach (var other_bounds in other.boundsList)
                    {
                        if (bounds.Contains(Position))
                            return true;
                    }
                }
                else
                {
                    if (Vector3.Distance(Position, other.Position) < 0.01f)
                        return true;
                }


            }

            return false;
        }

        public bool Intersects(OctreeBounds bounds) 
        {
            if (boundsList != null && boundsList.Count > 0)
            {
                for (int i = 0; i < boundsList.Count; i++)
                {
                    if (boundsList[i].Intersects(bounds))
                        return true;
                }
            }
            else
            {
                return bounds.Contains(position);
            }

            return false;
        }

        public bool Intersects(Ray ray, float distance,out IntersectInfo info) 
        { 
            if (boundsList != null && boundsList.Count > 0)
            { 
                IntersectInfo intersect = new IntersectInfo();
                bool hit = false;

                for (int i = 0; i < boundsList.Count; i++)
                { 
                    if (boundsList[i].IntersectRay(ray, out info, distance))
                    {
                        if (!hit)
                        {
                            intersect = info;
                            hit = true;
                        }
                        else
                        {
                            if (Vector3.Distance(ray.origin, intersect.point) > Vector3.Distance(ray.origin, info.point))
                                intersect = info;
                        } 
                    }
                }

                if (hit) 
                {
                    info = intersect;
                    return true;
                }

            }
        
            info = new IntersectInfo(); 
            return false;
        }

        internal void AddBounds(OctreeBounds bounds) 
        {
            if(boundsList == null)
                boundsList = new List<OctreeBounds>();
            if(!boundsList.Contains(bounds))
                boundsList.Add(bounds);
        }

        internal void RemoveBounds(OctreeBounds bounds)
        {
            if (boundsList == null)
                return;
            if (boundsList.Contains(bounds))
                boundsList.Remove(bounds);
        }

        public Bounds GetBounds() 
        { 
            if (boundsList != null && boundsList.Count > 0)
            {
                bounds = boundsList[0].GetBounds();
                for (int i = 1; i < boundsList.Count; i++)
                {
                    bounds.Encapsulate(boundsList[i].GetBounds());
                }
            }
            else 
            {
                bounds.center = Position;
                bounds.size = Vector3.zero;
            }

            return bounds;
        }

        public float ClosestDistance(Vector3 point) 
        {
            if (boundsList != null && boundsList.Count > 0)
            {
                float distance = Vector3.Distance(boundsList[0].ClosestPoint(point), point);

                for (int i = 1; i < boundsList.Count; i++)
                {
                    float d = Vector3.Distance(boundsList[i].ClosestPoint(point), point);
                    if (d < distance)
                        distance = d;
                } 

                return distance;
            }
            else { 
                return Vector3.Distance(point,Position);
            }

        }

        public Vector3 ClosestPoint(Vector3 point)
        { 
            if (boundsList != null && boundsList.Count > 0)
            {
                Vector3 p = boundsList[0].ClosestPoint(point);
                float d = Vector3.Distance(p, point);

                for (int i = 1; i < boundsList.Count; i++)
                {
                    Vector3 p1 = boundsList[i].ClosestPoint(point);
                    float d1 = Vector3.Distance(p1, point);
                    if (d1 < d) 
                    {
                        p = p1;
                        d= d1;
                    }
                }

                return p;
            }
            else
            {
                return Position;
            }
        }

        #endregion

        #region 生命周期

        private void OnEnable()
        {
            position = transform.position;
            OctreeManager.Add(this);
            Enable = true;
        }

        private void OnDisable()
        { 
            OctreeManager.Remove(this, updateBoundsOnRemove);
            Enable = false; 
        }

        #endregion



    }

}